import { iteratee } from 'lodash-es'

/**
 * 导出一个函数，用于获取指定日期所在周数
 * @param dateTime 指定日期
 * @returns
 */
export function getWeek(dateTime: Date) {
  // 创建一个新日期对象，用于操作
  const temp = new Date(dateTime.getTime())
  // 获取指定日期是星期几，0表示星期天，1表示星期一，以此类推
  const week = temp.getDay() || 7
  // 将日期设置为指定日期所在周的周一
  temp.setDate(temp.getDate() - week + 1 + 5)
  // 创建一个新日期对象，用于计算第一周的开始日期
  let firstDay = new Date(temp.getFullYear(), 0, 1)
  // 获取第一周的第一天是星期几
  const dayOfWeek = firstDay.getDay()
  // 计算第一周的开始日期
  let spandDay = 1
  if (dayOfWeek != 0) {
    spandDay = 7 - dayOfWeek + 1
  }
  firstDay = new Date(firstDay.getFullYear(), 0, 1 + spandDay)
  // 计算指定日期距离第一周开始日期的天数
  const d = Math.ceil((temp.getTime() - firstDay.getTime()) / 86400000)
  // 返回指定日期所在周数
  return Math.ceil(d / 7) + 1
}

export const formatDate = (date: Date, format: string) => {
  const we = date.getDay()
  const z = getWeek(date)
  const qut = Math.floor((date.getMonth() + 3) / 3).toString() // 季度
  const opt: { [key: string]: string } = {
    'Y+': date.getFullYear().toString(), // 年
    'm+': (date.getMonth() + 1).toString(), // 月(月份从0开始，要+1)
    'd+': date.getDate().toString(), // 日
    'H+': date.getHours().toString(), // 时
    'M+': date.getMinutes().toString(), // 分
    'S+': date.getSeconds().toString(), // 秒
    'q+': qut // 季度
  }

  // 中文数字 (星期)
  const week: { [key: string]: string } = {
    '0': '日',
    '1': '一',
    '2': '二',
    '3': '三',
    '4': '四',
    '5': '五',
    '6': '六'
  }
  // 中文数字（季度）
  const quarter: { [key: string]: string } = {
    '1': '一',
    '2': '二',
    '3': '三',
    '4': '四'
  }
  if (/(W+)/.test(format))
    format = format.replace(
      RegExp.$1,
      RegExp.$1.length > 1
        ? RegExp.$1.length > 2
          ? '星期' + week[we]
          : '周' + week[we]
        : week[we]
    )
  if (/(Q+)/.test(format))
    format = format.replace(
      RegExp.$1,
      RegExp.$1.length == 4 ? '第' + quarter[qut] + '季度' : quarter[qut]
    )
  if (/(Z+)/.test(format))
    format = format.replace(
      RegExp.$1,
      RegExp.$1.length == 3 ? '第' + z + '周' : z + ''
    )
  for (const k in opt) {
    const r = new RegExp('(' + k + ')').exec(format)
    // 若输入的长度不为1，则前面补零
    if (r)
      format = format.replace(
        r[1],
        RegExp.$1.length == 1 ? opt[k] : opt[k].padStart(RegExp.$1.length, '0')
      )
  }
  return format
}

/**
 * 用于格式化日期
 * @param value 日期值，可以是字符串、数字或Date对象，默认值为当前时间
 * @param format 格式化字符串，默认值为'YYYY-MM-DD HH:mm:ss'
 * @returns 格式化后的日期字符串
 * @example
 * dateFormat('2022-01-01 12:00:00', 'YYYY年MM月DD日 HH时mm分ss秒') // '2022年01月01日 12时00分00秒'
 */
export function dateFormat(
  // 日期值，可以是字符串、数字或Date对象，默认值为当前时间
  value: string | number | Date = Date.now(),
  // 格式化字符串，默认值为'YYYY-MM-DD HH:mm:ss'
  format: string = 'YYYY-MM-DD HH:mm:ss'
) {
  try {
    // 声明一个Date对象
    let date: Date
    // 判断日期值的类型
    if (typeof value === 'number' || typeof value === 'string') {
      // 如果是数字或字符串，则将其转换为Date对象
      date = new Date(value)
      // 判断转换后的Date对象是否有效
      if (isNaN(date.getTime())) {
        // 如果无效，则抛出错误
        throw new Error('日期格式不正确')
      }
    } else {
      // 如果是Date对象，则直接赋值
      date = value
    }
    // 定义一个函数，用于在数字前面补零
    const padZero = (val: number, len = 2) => {
      return val.toString().padStart(len, '0')
    }

    // 定义一个函数，用于替换格式化字符串中的占位符
    const replacement = (match: string) => {
      switch (match) {
        case 'YYYY':
          // 替换年份
          return padZero(date.getFullYear())
        case 'YY':
          // 替换两位年份
          return padZero(date.getFullYear()).slice(2, 4)
        case 'MM':
          // 替换月份
          return padZero(date.getMonth() + 1)
        case 'M':
          // 替换一位月份
          return String(date.getMonth() + 1)
        case 'DD':
          // 替换日期
          return padZero(date.getDate())
        case 'D':
          // 替换一位日期
          return String(date.getDate())
        case 'HH':
          // 替换小时
          return padZero(date.getHours())
        case 'H':
          // 替换一位小时
          return String(date.getHours())
        case 'mm':
          // 替换分钟
          return padZero(date.getMinutes())
        case 'm':
          // 替换一位分钟
          return String(date.getMinutes())
        case 'ss':
          // 替换秒
          return padZero(date.getSeconds())
        case 's':
          // 替换一位秒
          return String(date.getSeconds())
        case 'SSS':
          // 替换毫秒
          return padZero(date.getMilliseconds(), 3)
        default:
          // 默认返回原值
          return match
      }
    }
    // 使用正则表达式替换格式化字符串中的占位符
    return format.replace(
      /(YYYY|YY|M{1,2}|D{1,2}|H{1,2}|m{1,2}|s{1,2}|SSS)/g,
      replacement
    )
  } catch (e) {
    // 如果发生错误，则抛出错误
    throw new Error(`日期格式不正确:${e}`)
  }
}

/**
 * 时间问候语
 * @param param 当前时间，默认 new Date()
 * @returns 返回拼接后的时间字符串
 * @example
 * formatAxis(new Date()) // 上午好
 */
export function formatAxis(param = new Date()): string {
  const hour: number = new Date(param).getHours()
  switch (true) {
    case hour < 6:
      return '凌晨好'
    case hour < 9:
      return '早上好'
    case hour < 12:
      return '上午好'
    case hour < 14:
      return '中午好'
    case hour < 17:
      return '下午好'
    case hour < 19:
      return '傍晚好'
    case hour < 22:
      return '晚上好'
    default:
      return '夜里好'
  }
}

// 定义一个接口ILable，包含h、m、lable和flag四个属性
interface ILable {
  h: number
  m: number
  lable: string
  flag: boolean
}

/**
 * 根据当前时间返回问候语
 * @param date 当前时间，默认 new Date()
 * @returns 时间问候语
 */
export function clockAxis(date = new Date()): string {
  // 获取当前时间的小时和分钟
  const hour = date.getHours()
  const minute = date.getMinutes()
  // 定义一个数组lables，包含不同时间段对应的问候语和标志位
  const lables: ILable[] = [
    { h: 6, m: 0, lable: '凌晨好', flag: false },
    { h: 9, m: 0, lable: '早上好', flag: true },
    { h: 12, m: 0, lable: '上午好', flag: false },
    { h: 13, m: 30, lable: '中午好', flag: true },
    { h: 18, m: 0, lable: '下午好', flag: true },
    { h: 23, m: 59, lable: '晚上好', flag: true }
  ]
  // 在数组lables中查找当前时间对应的问候语
  const items = lables.find(
    (item) => hour < item.h || (item.flag && hour <= item.h && minute <= item.m)
  )
  // 返回对应的问候语，如果没有找到，则返回'早上好'
  return items?.lable || '早上好'
}

/**
 * 将时间转换为 `几秒前`、`几分钟前`、`几小时前`、`几天前`
 * @param param 时间，默认 new Date()
 * @param format 格式化时间，默认 'YYYY-mm-dd'
 * @returns 返回拼接后的时间字符串
 */
export function formatPast(param: string | Date, format = 'YYYY-mm-dd') {
  // 获取当前时间的时间戳
  let time = new Date().getTime()
  // 判断param的类型，如果是字符串则将其转换为时间戳，否则直接获取时间戳
  const t =
    typeof param === 'string' ? new Date(param).getTime() : param.getTime()
  // 计算当前时间与param的时间差
  time = Number.parseInt(`${time - t}`)
  // 如果时间差小于10秒，则返回'刚刚'
  if (time < 10000) {
    return '刚刚'
    // 如果时间差小于1分钟，则返回'x秒前'
  } else if (time < 60 * 1000 && time >= 10000) {
    return `${Math.floor(time / 1000)}秒前`
    // 如果时间差小于1小时，则返回'x分钟前'
  } else if (time < 60 * 60 * 1000 && time >= 60 * 1000) {
    return `${Math.floor(time / (60 * 1000))}分钟前`
    // 如果时间差小于1天，则返回'x小时前'
  } else if (time < 24 * 60 * 60 * 1000 && time >= 60 * 60 * 1000) {
    return `${Math.floor(time / (60 * 60 * 1000))}小时前`
    // 如果时间差小于30天，则返回'x天前'
  } else if (time < 30 * 24 * 60 * 60 * 1000 && time >= 24 * 60 * 60 * 1000) {
    return `${Math.floor(time / (24 * 60 * 60 * 1000))}天前`
    // 否则返回格式化后的时间
  } else {
    return dateFormat(new Date(t), format)
  }
}
